package com.xiaojie.importdata;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xiaojie.conf.ImportExportConf;
import com.xiaojie.context.DataInfoContext;
import com.xiaojie.dao.EDIDataOperate;
import com.xiaojie.error.FileFormatException;
import com.xiaojie.error.ImportException;
import com.xiaojie.ext.cust.CustImportExecute;
import com.xiaojie.importdata.handler.ReplaceFieldValueChain;
import com.xiaojie.importdata.handler.impl.ReplaceSelfRefFieldVale;
import com.xiaojie.parse.DataModelParse;
import com.xiaojie.parse.model.DataModel;
import com.xiaojie.parse.model.RefTable;
import com.xiaojie.parse.model.RelOtherSystem;
import com.xiaojie.parse.model.Table;
import com.xiaojie.toolkits.ZipUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 导入数据
 *
 * @author wangye
 * @classname ImportData
 * @date 2021/6/30 10:19
 **/
@Component
@Slf4j
public class ImportData {

    private final static Map<String, AutoGenerateField> AUTO_GENERATE_FIELD_MAP = Maps.newHashMap();

    @Autowired
    private DataModelParse parse;
    @Autowired
    private EDIDataOperate dataOperate;
    @Autowired
    private CreateInsertSql createInsertSql;


    @Autowired
    private ImportExportConf conf;
    @Autowired
    private ReplaceFieldValueChain replaceFieldValueChain;

    public void importZipFile(String configFilePath, File file, Map<String, Object> newFieldValueParams, String password) throws Exception {
        importZipFile(configFilePath, file, newFieldValueParams, password, null, null);
    }

    public void importZipFile(String configFilePath, File file, Map<String, Object> newFieldValueParams, String password,
                              CustImportExecute importExecute, Function<String, String> custGetFileContent) throws Exception {

        //1.解压文件到temp目录
        //目标文件夹
        String dest = file.getParent() + File.separator + System.currentTimeMillis();
        try {
            DataInfoContext.get().setDataModelInfo(DataModelParse.getDataModel(custGetFileContent, configFilePath, Boolean.FALSE));

            DataInfoContext.get().setNewFieldValueParams(newFieldValueParams);

            DataInfoContext.get().setImportTempDir(dest);

            Map<String, File> fileMap = ZipUtils.upZipFile(file, dest, password, "UTF-8").stream().collect(Collectors.toMap(File::getName, o -> o));
            DataInfoContext.get().setUnzipFileMap(fileMap);

            //2.处理解压文件进行导入
            //解析导入文件获取数据
            String fileName = Optional.ofNullable(DataInfoContext.get().getDataModelInfo())
                    .map(dataModel -> dataModel.getFileName()).orElse(null);
            File dataFile = fileMap.get(StringUtils.isNotBlank(fileName) ? fileName : conf.DEFAULT_DB_FILE_NAME);
            if (dataFile != null) {
                //导入数据库文件
                doImportDBData(dataFile, importExecute);
            }
            //导入关联系统
            Map<String, RelOtherSystem> relOtherSystems = DataInfoContext.get()
                    .getDataModelInfo().getRelOtherSystem();
            if (relOtherSystems != null) {
                importRelOtherSystem(fileMap, relOtherSystems);
            }
        } catch (ImportException e) {
            throw e;
        } catch (FileFormatException e) {
            log.error(e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        } finally {
            //刪除临时文件
            FileUtil.del(dest);
            //清除上下文数据
            DataInfoContext.remove();
        }
    }

    /**
     * 关联系统导入
     *
     * @param fileMap
     * @param relOtherSystems
     */
    private void importRelOtherSystem(Map<String, File> fileMap, Map<String, RelOtherSystem> relOtherSystems) {
        //获取存储新旧数据容器
        Map<String, Map<String, Map>> correspondFieldsMap = Optional.ofNullable(DataInfoContext.get().getCorrespondDataTableFieldMap()).orElse(Maps.newHashMap());

        for (String relSysName : relOtherSystems.keySet()) {
            File relSysZip = fileMap.get(relSysName + ".zip");
            if (relSysZip == null) {
                log.error("导入跳过：关联系统" + relSysName + ",文件未导出");
                continue;
            }

            RelOtherSystem relOtherSystem = relOtherSystems.get(relSysName);
            String importApi = relOtherSystem.getImportUrl(relSysName);

            //发送参数和zip文件给关联系统

            //获取参数
            Map<String, Object> httpPrams = new HashMap<>();
            relOtherSystem.getParams().entrySet().forEach(
                    e -> {
                        Map m = (Map) httpPrams.computeIfAbsent(e.getKey(), k -> new HashMap<>());
                        List<RefTable> refTableList = e.getValue();
                        for (RefTable refTable : refTableList) {
                            String tableName = refTable.getTableName();
                            Map<String, Map> replaceMap = correspondFieldsMap.get(tableName);
                            if (replaceMap != null) {
                                Map old2New = replaceMap.get(refTable.getRefField());
                                if (old2New != null) {
                                    //如果其他系统的关联参数有变化 则放入参数
                                    m.putAll(old2New);
                                }
                            }
                        }
                    }
            );

            HttpResponse response = null;
            try {
                log.error(relSysName + "调用参数：" + JSON.toJSONString(httpPrams));
                response = HttpRequest.post(importApi)
                        .form("json", JSON.toJSONString(httpPrams))
                        .form("file", relSysZip)
                        .timeout(5 * 60000)
                        .execute();
                //校验导入是否成功
                checkImportRelSysResult(relSysName, response);
            } catch (Exception e) {
                log.error(relSysName + "导入失败");
                log.error(e.getMessage(), e);
                throw new ImportException("导入失败:关联系统" + relSysName + "导入失败");
            }
        }
    }

    /**
     * 校验导入其他系统是否成功
     *
     * @param relSysName
     * @param response
     */
    private void checkImportRelSysResult(String relSysName, HttpResponse response) {
        //校验标准：返回Json字符，{code=200}
        String body = response.body();
        if (JSONUtil.isJson(body)) {
            JSONObject object = JSONObject.parseObject(body);
            String code = object.getString("code");
            if ("200".equals(code)) {
                return;
            }
        }
        throw new ImportException(relSysName + "返回结果错误,导入失败" + body);
    }

    private void doImportDBData(File dataFile, CustImportExecute importExecute) throws ClassNotFoundException {

        importExecute = importExecute == null ? new CustImportExecute() {
        } : importExecute;

        String json = FileUtil.readString(dataFile, "UTF-8");
        Map<String, List<Map>> dataMap = JSON.parseObject(json, new TypeReference<Map<String, List<Map>>>() {
        });

        DataModel dataModel = DataInfoContext.get().getDataModelInfo();
        Map<String, Table> tables = dataModel.getTables();

        importExecute.beforeCreateNewData().execute(dataModel, dataMap);

        //替换非依赖(依赖本表或它表字段)字段值
        replaceFiledValue(dataMap, tables);

        //记录已生成新数据的表名
        List<String> completedTableNameList = Lists.newArrayListWithCapacity(tables.size());
        //记录新生成的数据
        Map<String, List<Map>> newDataMap = Maps.newHashMap();
        //替换关联字段值
        replaceRefFieldValue(dataMap, tables, completedTableNameList, newDataMap);

        //扩展点，生成新数据后扩展功能
        importExecute.afterCreateNewData().execute(dataModel, newDataMap);

        //生成插入的sql语句
        List<String> insertSqlList = createInsertSql.createInsertSql(newDataMap, new ArrayList<>(dataModel.getTables().keySet()));

        //保存数据
        for (String sql : insertSqlList) {
            dataOperate.insert(sql);
        }
    }

    private void replaceFiledValue(Map<String, List<Map>> dataMap, Map<String, Table> tables) {
        for (Map.Entry<String, Table> entry : tables.entrySet()) {
            String tableName = entry.getKey();
            Table table = entry.getValue();
            //获取该表数据，为空跳过
            List<Map> tableDataList = dataMap.get(tableName);
            if (CollectionUtil.isNotEmpty(tableDataList)) {
                for (Map rowData : tableDataList) {
                    replaceFieldValueChain.replaceFiledValue(tableName, table, rowData);
                }
                //将替换好的数据放回容器中
                dataMap.put(tableName, tableDataList);
            }
        }
    }

    private void replaceRefFieldValue(Map<String, List<Map>> dataMap, Map<String, Table> tables, List<String> completedTableNameList, Map<String, List<Map>> newDataMap) {
        for (Map.Entry<String, Table> entry : tables.entrySet()) {

            String tableName = entry.getKey();
            Table table = entry.getValue();

            //获取该表数据，为空跳过
            List<Map> tableDataList = dataMap.get(tableName);
            if (CollectionUtil.isNotEmpty(tableDataList)) {
                for (Map rowData : tableDataList) {
                    replaceFieldValueChain.replaceRefFiledValue(tableName, table, rowData);
                }
                //表内字段关联的值可能不在同一行数据，如树形结果数据，所以需要表中所有行数据字段值替换为新值后，再替换表内相互依赖字段的值
                Map<String, String> selfRelFieldMap = table.getSelfRelFields();
                if (MapUtil.isNotEmpty(selfRelFieldMap)) {
                    for (Map rowData : tableDataList) {
                        //替换关联本表字段值
                        ReplaceSelfRefFieldVale.getInstance().replaceFieldValue(tableName, table, rowData);
                    }
                }
            }
            // 数据为空也添加到已新增表里 -> 1.保证关联该表的表可以正常进行处理
            newDataMap.put(tableName, tableDataList);
            completedTableNameList.add(tableName);
        }
    }


}